package com.google.code.virtualhockey.vhx.server.servlet.rest

import javax.servlet.http.{HttpServletResponse, HttpServletRequest, HttpServlet}
import com.google.code.virtualhockey.vhx.util.LangUtils
import java.util.logging.Logger
import java.io.{PrintWriter, OutputStreamWriter}

/**
 * Basic servlet that is responsible for processing REST style commands.
 */
abstract class RestCommandServlet extends HttpServlet {

  protected val _log = Logger.getLogger( classOf[RestCommandServlet].getName )

  /**
   * Defines the class to be used to lookup command handlers.
   */
  protected val _cmdHandlerLookup: RestCommandHandlerLookup

  /**
   * Only POST requests are accepted!
   */
  override def doPost( req: HttpServletRequest, resp: HttpServletResponse ) { processRequest( req, resp ) }

  /**
   * Processes all incoming requests.
   */
  protected def processRequest( rq: HttpServletRequest, rp: HttpServletResponse ) {
    val cmdrq = new RestCommandRequest( rq, rp )
    val out = new PrintWriter( new OutputStreamWriter( rp.getOutputStream(), "UTF-8" ), true )
    var strJson = ""
    
    rp.setContentType( "application/json;charset=utf-8" )

    // process the command
    //
    try {
      val handler = _cmdHandlerLookup.getHandler( cmdrq )

      _log.entering( handler.getClass.getName, "process" )
      val strResult = handler.process( cmdrq )
      _log.exiting( handler.getClass.getName, "process" )

      strJson = processSuccessResponse( cmdrq, strResult )
    } catch {
      case t: Throwable => {
        _log.throwing( getClass.getName, "processRequest", t )
        val listJson = createErrorResponseAttrs( cmdrq, t )
        
        strJson = listJson.mkString( "{", ",\n", "}" )
      }
    }

    // send the response back to the client
    //
    LangUtils.withResource( out ) {
      _.write( strJson )
    }
  }

  /**
   * Processes a success response.
   *
   * @param rq the current request
   * @param strResult the JSON response that has been computed by the command handling process
   * @return the processed JSON response String
   */
  protected def processSuccessResponse( rq: RestCommandRequest, strResult: String ): String = {
    val attrs = List(
      LangUtils.toJsonAttribute( "cmd", rq.optCommand.getOrElse( "None" ) ),
      LangUtils.toJsonAttribute( "status", "OK" ),
      "\"result\":" + strResult
    )
    
    attrs.mkString( "{", ",\n", "}" )
  }

  /**
   * Creates a list of JSON attributes that are included in all command responses following an error or
   * exception that caused the command processing to be aborted.
   *
   * @param rq the current request
   * @param t the thrown exception
   * @return a List of attributes
   */
  protected def createErrorResponseAttrs( rq: RestCommandRequest, t: Throwable ): List[String] = {
    var attrs = List(
      LangUtils.toJsonAttribute( "cmd", rq.optCommand.getOrElse( "None" ) ),
      LangUtils.toJsonAttribute( "status", "ERROR" ),
      LangUtils.toJsonAttribute( "errType", t.getClass.getSimpleName ),
      LangUtils.toJsonAttribute( "errMsg", t.getMessage ),
      LangUtils.toJsonAttribute( "errTrace", t.getStackTraceString )
    );

    if ( t.isInstanceOf[RestCommandException] ) {
      val ex = t.asInstanceOf[RestCommandException]
      attrs ++= ex.getAdditionalResponseAttributes
      rq.httpResponse.setStatus( ex.httpStatusCode )
    } else
      rq.httpResponse.setStatus( HttpServletResponse.SC_INTERNAL_SERVER_ERROR )

    attrs
  }
}
